UI Components

Mac-Style Cover Flow Photo Gallery with CSS Scroll Snap

By Aigars Silkalns Updated 8 min read

The Mac-style photo gallery, the one where covers fan out in 3D and the centre image faces you, no longer needs Photoshop mock-ups, a Flash component or an XML file. Today it is a scrolling list, CSS scroll snap and a scroll-driven animation: a few dozen lines of CSS and no required JavaScript.

This tutorial builds that gallery from scratch. You get a coverflow that works with touch, mouse wheel and keyboard, tilts each cover as it moves through the centre, and degrades to an ordinary swipeable row where the newest CSS is not available.

A row of landscape and city photos in 3D perspective, the centred brick alley photo facing forward and the others angled away with soft reflections beneath
The finished gallery: the centred cover faces you, its neighbours tilt back, and the reflection is optional polish.

How the original worked, and why it had to go

The 2007 version drew the perspective look in Photoshop with Warp transforms, then relied on a third-party Flash component that loaded photos from an XML file. Adobe ended support for Flash Player at the end of 2020 and browsers removed it. The effect is still worth building as a compact way to browse albums, products or posters, and now the browser does the 3D work while the images stay real images that search engines and screen readers can read.

The building blocks

Four CSS features do the work. None of them need a library.

  • Scroll snap turns a plain horizontally scrolling list into something that always settles on one item. It is widely available across browsers and is what gives the gallery its tactile, one-cover-at-a-time feel.
  • 3D transforms with perspective tilt and push covers back in depth. perspective goes on the scroll container, and the items are positioned in 3D space inside it.
  • Scroll-driven animations link an animation to scroll position instead of time. With animation-timeline: view() each cover animates according to where it sits in the scrollport: entering, centred, or leaving. This is the part that replaces the Flash component. It works in Chrome and Edge from version 115 and in Safari 26. Firefox enables it by default from version 159; at the time of writing that release is in beta and Firefox 158 and earlier ignore the property. Because it is not yet Baseline, the demo treats it as an enhancement.
  • -webkit-box-reflect draws the Apple-style reflection. It is non-standard but works in Chromium and Safari. Browsers that lack it simply show no reflection.

Step 1: Mark up the gallery as a list

A gallery is a list of images, so use one. Each item wraps a single img in a span that we will animate. The scroller needs tabindex="0" so keyboard users can focus it and scroll with the arrow keys, and a label so a screen reader announces what it is.

<div class="flow" tabindex="0" role="region" aria-label="Album covers">
  <ul class="track">
    <li class="cover"><span class="card"><img src="covers/01.jpg" alt="Blue fjord seen from a rocky cliff edge" width="520" height="520"></span></li>
    <li class="cover"><span class="card"><img src="covers/02.jpg" alt="Green hills and a winding road under a cloudy sky" width="520" height="520"></span></li>
    <!-- more covers -->
  </ul>
</div>

Write real alt text for each photo. Because the images are normal elements, every one is available to assistive technology whether or not the 3D effect runs.

Step 2: Build the scroller and the track

The outer .flow element scrolls and owns the perspective. Inside it, .track is a flex row that preserves 3D, so covers sort by depth and a closer cover correctly overlaps a farther one.

:root {
  --cover: min(52vw, 260px);
}

.flow {
  width: 100%;
  max-width: 1100px;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  perspective: 900px;
  perspective-origin: 50% 40%;
  scrollbar-width: none;
}
.flow::-webkit-scrollbar {
  display: none;
}
.flow:focus-visible {
  outline: 3px solid oklch(0.8 0.15 230);
  outline-offset: -6px;
}

.track {
  display: flex;
  width: max-content;
  margin: 0;
  /* Let the first and last cover reach the centre. */
  padding: 3rem calc(50% - var(--cover) * 0.35) 5rem;
  list-style: none;
  transform-style: preserve-3d;
}

Two details deserve explanation. The horizontal padding is half the scroller width minus half a cover (adjusted for the overlap below), which is what lets the first and last covers scroll all the way to the middle. And :focus-visible gives keyboard users a clear outline without showing it on every mouse click.

Step 3: Snap one cover at a time

Each li is a snap target aligned to the centre, and a negative inline margin pulls neighbours together so they overlap like a fanned stack.

.cover {
  flex: 0 0 var(--cover);
  width: var(--cover);
  margin-inline: calc(var(--cover) * -0.15);
  scroll-snap-align: center;
  scroll-snap-stop: always;
  cursor: pointer;
  transform-style: preserve-3d;
}

/* A span is inline, and inline elements ignore transforms. */
.card {
  display: block;
}

.cover img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 6px;
  box-shadow: 0 20px 40px rgb(0 0 0 / 0.35);
  -webkit-box-reflect: below 2px linear-gradient(transparent 65%, rgb(0 0 0 / 0.35));
}

scroll-snap-stop: always stops a hard flick from skipping several covers, which suits a gallery where each item is worth seeing. Notice that the snapping element, the li, is not the element we transform. That is deliberate. Browsers calculate snap positions from an element’s box, and if the same element also scales and rotates as it scrolls, the snap point drifts and the centred cover ends up off-centre. Keeping the transform on an inner .card keeps snapping stable. The height: auto on the image lets aspect-ratio win over the height attribute, which would otherwise stretch each photo. The display: block on .card matters too: it is a span, which is inline by default, and transforms do not apply to inline elements.

Step 4: Tilt covers with a scroll-driven animation

Now the coverflow effect. The animation runs on .card and uses view(inline) as its timeline. Progress is 0% when a cover enters the scrollport, 50% when it is centred, and 100% when it leaves. The keyframes map those positions to a tilt: angled one way on the right, flat and pushed forward in the middle, angled the other way on the left.

@supports (animation-timeline: view()) {
  .card {
    animation: flip linear both;
    animation-timeline: view(inline);
  }
}

@keyframes flip {
  0%, 40% {
    transform: translateX(calc(var(--cover) * 0.12)) translateZ(-150px) rotateY(58deg);
    filter: brightness(0.55);
  }
  50% {
    transform: translateZ(120px);
    filter: brightness(1);
  }
  60%, 100% {
    transform: translateX(calc(var(--cover) * -0.12)) translateZ(-150px) rotateY(-58deg);
    filter: brightness(0.55);
  }
}

The flat stretches at 0 to 40% and 60 to 100% are what make the tilt look like a fixed pose. Without them, covers far from the middle would keep rotating as you scroll. The side covers also darken a little, which pushes them back visually and keeps the centre cover the focus. Because the animation is tied to scroll position and not a clock, there is nothing to start, stop or sync: the scroll position is the single source of truth.

The @supports wrapper is the fallback. Where scroll-driven animations are missing, the cards never animate, and visitors get a flat, snapping row of covers that still scrolls and swipes properly.

The same coverflow at phone width, with the brick alley photo large in the centre and neighbouring photos tilted at the edges of the screen
At phone width the cover size shrinks with the viewport and the gallery still centres on one item.

Step 5: Let people click a cover to bring it forward

Scrolling covers a lot, but the original Apple interface also let you click a side cover to bring it to the middle. That needs a few lines of JavaScript, using scrollIntoView so the snap points do the rest.

const flow = document.querySelector('.flow');
const reduceMotion = matchMedia('(prefers-reduced-motion: reduce)');

flow.addEventListener('click', (event) => {
  const cover = event.target.closest('.cover');
  if (!cover) return;
  cover.scrollIntoView({
    behavior: reduceMotion.matches ? 'auto' : 'smooth',
    inline: 'center',
    block: 'nearest',
  });
});

With the track padding from Step 2, the first cover starts centred, so you do not need to set an initial position. To open on a different cover, call scrollIntoView on it once at load, for example document.querySelector('.cover:nth-child(5)').scrollIntoView({ inline: 'center', block: 'nearest' }). The gallery already works without this script, so treat it as a progressive enhancement. If a cover should open a larger view, hand it off to a lightbox; our modern lightbox tutorial covers that pattern.

Accessibility and motion

A 3D carousel is a lot of movement, so respect people who have asked their system for less of it. The scroll-driven tilt is driven by the user’s own scrolling, but a strong rotation can still be uncomfortable. Switch it off for people who set the reduced-motion preference. This media query is widely available.

@media (prefers-reduced-motion: reduce) {
  .card {
    animation: none;
  }
}

With the tilt off, the gallery is still a clean snapping row. A few more rules keep it usable.

  • Keep the scroller focusable and labelled so keyboard users can reach it, and keep the visible focus outline.
  • Give every image meaningful alt text and explicit width and height to avoid layout shift while photos load.
  • Do not put essential text only on the side covers. Anything dimmed and angled is harder to read, so reserve captions for the centre item or a caption area below the gallery.
  • Consider adding previous and next buttons if the gallery is a primary way to navigate, because not everyone discovers horizontal scrolling.

Browser support at a glance

Feature Status If unsupported
Scroll snap Widely available Not applicable, the row still scrolls
3D transforms and perspective Widely available Not applicable
Scroll-driven animations (view()) Not yet Baseline: Chrome and Edge 115+, Safari 26+, Firefox 159+ (beta at time of writing) Flat snapping row, no tilt
-webkit-box-reflect Non-standard; Chromium and Safari No reflection
prefers-reduced-motion Widely available Not applicable

The reflection is the only non-standard piece, and the page loses nothing important without it. If you want a reflection everywhere, you can draw one yourself with a flipped, masked copy of the image, at the cost of duplicate markup or a CSS variable holding the image URL.

Make it your own

All the numbers sit in a handful of places, so tuning the feel is quick.

  1. Spacing: change the negative margin-inline on .cover. More overlap makes a tighter stack; less shows more of each side cover. If you change it, adjust the track padding by the same amount so the first cover still reaches the centre.
  2. Angle and depth: edit rotateY and the translateZ values in the keyframes. Larger angles feel more dramatic but make side covers harder to read.
  3. Perspective: a lower perspective value exaggerates depth. Around 800px to 1200px looks natural for covers this size.
  4. Cover size: change --cover. It uses min() so covers shrink on small screens.

If a full 3D coverflow is more than your design needs, simpler options exist. Our coverflow image carousel snippet is a ready-made version, the scroll snap card slider shows the flat approach, and the photo gallery with modal lightbox is a better fit when people need to inspect each photo. To go deeper on the transforms used here, start with Easy CSS Animation With Transition and Transforms, and for polishing the images themselves, see CSS3 Image Styles.

Frequently asked questions

Do I still need Flash or a plugin for a cover flow gallery?

No. Flash is gone from browsers, and the effect is plain CSS now. Scroll snap handles the stepping, 3D transforms handle the look, and a scroll-driven animation connects the two.

Why animate an inner element instead of the list item?

Snap positions are calculated from an element’s box. If the snapping element also scales and rotates while scrolling, the snap position moves and the centred cover drifts off-centre. Transforming an inner wrapper keeps the snap target steady.

What happens in a browser without scroll-driven animations?

The @supports rule skips the animation and visitors get a flat, snapping row of covers. It is less dramatic, but every image is still reachable by swipe, scroll wheel and keyboard.

102 comments from the archive
  1. First of all, I’d like to congratulate you on the fabulous design job with webdesignerwall. I’m loving this photoflow component and love the stuff you’ve done with it. Since its’ xml, I’m sure we can link each image to a post or page on the site as well! Thanks for the tutorial and I might do one for my site for easy navigation of posts, once I can afford this component

  2. Great job again.

    I read somewhere that in Flash, to get rid of that aliasing when an image animates, you had to export in “High” quality and not “Maximum” quality. I once created a carrousel with external images through a XML file (just like you do) and exported in “Maximum” quality, and it looked very aliased.

    In any case, it looks wonderful, I will surely use it.

    I sent you an email to congratulate you on this great website, and I’ll say it again: I love this site. Keep it going on.

    Jeremy Thomas

  3. How can i use different xml files? If i want to load different gallerys with different xml, where can i specify the xml filename? Thanks.

  4. Ok i have searched and I must just be stupid but…
    How do you create a grid!

  5. Nice indeed. But just so you know that credit should be given to this guy who created COVER FLOW back in the day. Apple bought it from him and have used it everywhere from iTunes to the iPhone :-)

    Cheers

  6. Great job, I need a better photo flash gallery then the ones I use for my friends. Currently I am using slide show pro which is great and so is simple viewer but this looks a lot better and easier to use.

  7. Ho my god!….wonderful!….i love your art works….i’m a Fan of you!….even tought…the gallery miss of a link to navigate back and forward into the images…
    But it’s wonderful!
    Compliments!

  8. @huey – Thanks for your information. I didn’t know that CoverFlow was originally created by steelskies.com

  9. I like the tutorial, my only problem with it, and the components listed in the comments is that they are not accurate, they simply skew/mask the images, rather than actually placing them in a perspective. Most people probably wouldn’t know the difference, but it is noticeable.

  10. Though quietlyscheming’s DisplayShelf is good N99 is better because you can use your own images and just XML editing.Also the images move both ways with just the scroll wheel of the mouse itself.

  11. I love it!!
    But how do i add links to the pics? {without the flash component but via the XML file}

    Cheers

  12. @Chike – To add a link to the image via XML, please read the user guide at Flashloaded: (under Using XML)

  13. Not only is this tutorial simply amazing, letting us in on the “secrets” of the pro’s – but giving it away to the masses makes us all better designers. Thank you.

  14. Could anyone please tell me how to make a thumbnail grid in Photoshop? I’ve searched Google with no help. I’d really appreciate the tip. Thanks.

  15. you forgot a step (right before step 4)…
    in your picture the TOP of the image also has a gradient mask/gradient overlay…
    because its faded out. maybe adding that step woulda helped us who follow strictly by what the tutorial says ;)

  16. I want photoflow, it will be useful for my firefox gallery (I develop firefox themes :D)
    Nice work on your website ^^

  17. I believe you can make a thumbnail grid under the file–>automate–>contactsheet or something like that. I’m at work right now, so I can’t confirm. You still might have to do some editing afterwards. Just one easy way.

  18. thanks for your great work, things like this make internet more friendly, i think so; for us… the begginers in web design.

  19. Really really nice ive always wondered how to create something like that on photoshop

  20. great stuff! how do you add a txt description to the image? something through the xml right? and adding dynamic txt box in flash right?

  21. nice tutorial ,

    just confuse how to upload this .swf on the internet..im upload but no picture appear..what should i do??…something missing on my component or my uploading step..

    really need a help..

  22. amazing tutorial and download. xml photo galleries are making this technology more accessible to the common artists that might have perhaps a little technological knowledge and a lot of artistic vision/ambition. thank you for taking the time to put this together.

  23. Can you do update the code to allow autoscrolling of thumbnails when we point the mouse cursor to the left or to the right side? It would be perfect with this option added! Also maybe add comments in bottom… Like (picture details like description, exposure, aperture,…). TY for sharing with us :)

  24. o0k well, how can i give a link on active photos? for example, there will be some photos at bottom side of page that active, and we click on them to change photos. on a photo, i wanna give a link by clicking to go a another else web site. i dunno how to do that :( please help me!

    regards!

  25. Since when do they charge for stuff on Kirupa.com (the photoflow component you have to download) – gah! Has anybody got a tutorial to write this not as a component?

  26. [email protected] –
    Since it is an embedded flash application you will either have to create a movieclip in Flash (you can do this on the Flash timeline or by loading a movieclip externally onto the root) OR by using –

    on(release) {
    getURL(www.example.com, “_blank”);
    }

    on a button or movieclip in Flash.

    podtim –
    The swf will get the images externally just like a browser will, so any files that are referenced in the xml file must be in the same place relative to the swf file. In other words, if your images are referenced as being in the ‘images’ folder, when you upload your swf you must also upload you images in a folder called ‘images’ in the same directory as the swf file. Also, your xml file must be present in order for the swf to get the image files. Upload everything and see if it works.

  27. I love this! I’ve been looking for a tut on this type of navigation since I saw it on the new iphone. I’m not pro, but I will be someday. I’m trying to teach myself everything I can and get all the programs possible to do that. Is there a way to perhaps rerun the contest? With two kids to homeschool, I’m broke lol. I’d love, and use, the photoflow!

  28. I agree with the person who was confused about how to create a thumbnail grid. Everything seems straightforward, but there seemed to be some giant leaps over how to do that. My Google search for creating one didn’t bring up any signigicant results either. Please help. Thanks, Monica

  29. I think to create a thumbnail grid the easiest way is get some images and make a square out of them (3 across, 3 down) put them into a layer set, then duplicate that layer set and move it around your page until the layer sets take up the size of your background.

    Excellent tutorial, many thanks

  30. Thanks a ton for this tutorial. I don’t know if I would ever use it, as I’m not too into Flash stuff, but it looks pretty rad. Keep posting unique tutorials like this one.

  31. ok, so I’m downloading ps cs3 now to do the wrap effect because its not on ps 7 lol… but i just bought the photo flow for flash 8.. and its being shipped does anyone know how long it takes to ship this thing?

  32. could you please define what is the size of the thumbnail grid photo and if possible how to quickly make one in Photoshop maybe?

  33. Hi Guys, I am very late to enter the contest of getting photoflow component but would any of you guys be soo kind to give me a copy of this component please? :) I know its cheeky to as but by looking at the example, this looks soo professional and would like to do my own photo gallery websites. Any help appreciated, many thanks, Sahid.

  34. It looks great! I would love to include PhotoFlip onto my WordPress Site. How to I incorporate it onto wordpress? What I would liek to do, is to 1st get the demo version working on my wordpress site to make sure I know how to, before I design my own PhotoFlip with the guide given above.
    Any ideas, I am not totally knew to wordpress, but don’t understand that much about coding.

  35. How can i insert an external link (a href=”http://www.google.com/”) to each picture ?

  36. awesome .. nice tips…. i find only one issue that it takes static images. It would be great if it take images from rss feeds (e.g. from flickr).

  37. Flash experts from india!
    Flash Specialist for building full flash websites.

  38. Hi Love your flash but have a question (not so good in flash myself you c): Was wondering if it is posible to make a horizontal scroller so that you don’t need to click on every picture to get the next one? If there is would you tell me how to please?
    Kind Regards

  39. Hi Love your flash but have a question (not so good in flash myself you c): Was wondering if it is posible to make a horizontal scroller so that you don’t need to click on every picture to get the next one?

  40. never mind, fixed it. Img size was the problem. If an image is smaller then the size you want it wont appear

  41. never mind, fixed it. Img size was the problem. If an image is smaller then the size you want it wont appear

  42. Me pueden ayudar a saber como hacer esta Galeria en la web Desafio Esteli?

  43. What an awesome Tool! Im playing around with it as we speak. One question though. How do I change the background? Currently it set on a mosaic design, I want it to be all black. How do I go about doing this? Thanks a lot!

    Francis

  44. Hey!! Man, WOW!! that’s a great tut there. You are a GOD of designing. I tried this ,got the component dragged it , modified the properties as you’ve showed here in the tut, but…… after publishing the images are not displyed. I can only see white , blank squares, I can flip them, though.
    What the prob could be? I’m using Flash 8 version. Please reply on my e-mail id [email protected] .I need it very badly!!.
    Thanks.

  45. All I can say is overexposure to pure Awesomeness….!!! great tutorial…keep up the good work.

  46. Very pretty but is it possible to put a link on each photo?
    i search to modify the xml but i didn’t found anything
    thanks

  47. if anyone needs the photoFlow…i have it so just send me an email and i will send it to you.

  48. Wicked version on photoflow mate, well done. Is there any chance of grabbing the the .fla file off you to learn how its done… Understandable if you cant.

    Thanks

    Les

  49. Can someone help me with this gallery, i designed my site in flash, now i want to finish it by adding the gallery, it’s a portfolio site, but I don’t know how to use this. COULD SOMEONE PLEASE HELP :(

    [email protected]

  50. Hi,
    Can you send me the Photoflow please? Thanks. My email address is [email protected]

    How to take away the wall behind it? I just want black or white wall behind it? Can you do it in Flash 9?

    How to link the images to weblinks?

    Please help, Thanks.

  51. Hi,

    This is wonderful stuff. Love to use it for a website. Many I know is’t okay to use it? Please let me know.

  52. The designs showed here show what simple and tasteful design is all about. Another one to consider

  53. are you paid by Flashloaded?

    doing such a tutorial is a joke. this is just comercial advertising for flashloaded. if you would show how to build this in as2 or 3 then i would say wow…. but this tastet just bad…

  54. Hii , thanks for this great gallery
    Is there a way to change the background image in the free demo you gave here for download?
    Thanks

  55. Hi.
    Das Teil ist ja absolut genial.
    Kann ich die FLA dazu bekommen?

    Liebe Grüße
    RM

  56. I learnt my picture editing using fireworks 8 but i can navigate my way decently around photoshop. however, i dunno how to create the thumbnail grid in photoshop. could you please include a tutorial for that as well? thank you

  57. It relies heavily on touch because the pages I have a user acting on, are usually a collection of child records. uggs outlet Caching is a big help in this respect.Thanks

  58. If I got a chance, i will prefer buying the iphone 4 white but not the iphone 4 Black. Who can tell me where is the white iphone 4 available? I would really want to take one.

  59. It’s sorry to hear that Some white iPhone 4 buyers have reported signal reduction when the phone is held in certain ways, especially in the left hand, as the antenna problem is in the bottom left corner of the phone’s side casing. Is that mean i have to wait longer to get the white iphone 4?

  60. Always a pleasure reading your sophisticated-ly engineered articles.

  61. Make a bored visitor into a customer. Custom website we design will convey a strong image for your customers.

    Web Application Design

  62. Another questionnaire, the participants’ problem identification and structuring, idea generation, problem elaboration and clarification, such as creativity, problem solving insertion sezkin idea which one’s preferred styles are evaluated. While participants preferred the style of non-disclosure and intellectual development of ADHD, with ADHD, participants chose to produce ideas.

  63. OH,it’s so helpful to me,and your blog is very good,I just couldnt leave your web site prior to saying that I definitely loved the quality facts you.You all do this kind of a great job at these Concepts… cannot tell you how significantly I, for one particular appreciate all you do!

  64. Magnificent goods from you, man. I have understand your stuff previous to and you are just extremely fantastic. I really like what you’ve acquired here, certainly like what you’re stating and the way in which you say it. You make it enjoyable and you still take care of to keep it sensible. I cant wait to read much more from you. This is actually a wonderful website. Kind regards, Rudy James

    1. Hi,
      I understands the tutorial but upto photoshop part. I have added component as well in flash but now how to write xml for this? please help me.

  65. With havin so much written content do you ever run into any problems of plagorism or copyright violation? My site has a lot of exclusive content I’ve either created myself or outsourced but it appears a lot of it is popping it up all over the internet without my permission. Do you know any solutions to help prevent content from being stolen? I’d genuinely appreciate it.

  66. Hi, very nice design and still up to date !!

    I would like to use your gallery for my private website. can u send me the fash files (fla) pls, if u still have it.

    to [email protected]

    have a nice day :)

  67. you may have an incredible blog here! would you prefer to make some invite posts on my weblog?

  68. hello, thanks alot for this tutorial !! Please i would like to know how did you create the thumbnail grid using multiple photos. did you use photoshop? I need to do the same type of grid. Id appreciate any advise. Thanks alot

    [email protected]

  69. Hey not to set off topic but can anyone give me overview of. New York Car Insurance Reform 295 Greenwich St, New York, NY 10007 (646) 351-0824 They are down the block with me. I was wondering if they were a good insurance company. I need to acquire coverage, it is the law you know, but I have to have a good price price plus I want friendly service.

  70. Wow – this is a great step-by-step. You really know how to use the products for great effect *sigh*. I love how you warp the image then apply the spotlight effect. I would never have come up with something like that. Any chance of getting hold of a few stem cells to give myself a cerebal upgrade? I’m only a man anyway so I have a decent excuse :) Well done and great work.

  71. My site has a lot of exclusive content I’ve either created myself or outsourced but it appears a lot of it is popping it up all over the internet without my permission. Do you know any solutions to help prevent content from being stolen?

Comments are closed on this article.